/*
 * Copyright (C) 2012 praetorian droid
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <jni.h>

#include <GLES/gl.h>
#include <GLES/glext.h>

#include <sys/time.h>

#include "common.h"
#include "glu/glu.h"

static GLScreen *gScreen;
static Brick gBrick;
static Ball gBall(gBrick);
static Wall gWall;
static Platform gPlatform;
static GLObject *gStuff[] = { &gBall, &gBrick, &gWall, &gPlatform };

static JavaVM *gJavaVM;
static jobject gCallbackObject = NULL;

static void collisionCallback(int type) {
    int status;
    JNIEnv *env;
    bool isAttached = false;

    if (!gCallbackObject) return;

    if ((status = gJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4)) < 0) {
        LOGE("collisionCallback: failed to get JNI environment");
        if ((status = gJavaVM->AttachCurrentThread(&env, NULL)) < 0) {
            LOGE("collisionCallback: failed to attach current thread");
            return;
        }
        isAttached = true;
    }

    jclass cls = env->GetObjectClass(gCallbackObject);
    if (!cls) {
        LOGE("collisionCallback: failed to get class reference");
        if (isAttached) gJavaVM->DetachCurrentThread();
        return;
    }

    jmethodID method = env->GetMethodID(cls, "callback", "(I)V");
    if (!method) {
        LOGE("collisionCallback: failed to get method ID");
        if (isAttached) gJavaVM->DetachCurrentThread();
        return;
    }

    env->CallVoidMethod(gCallbackObject, method, type);

    if (isAttached) gJavaVM->DetachCurrentThread();
}

GLScreen::GLScreen(int width, int height): mWidth(width), mHeight(height) {
    mRatio = (float) height / width;

    glEnableClientState(GL_VERTEX_ARRAY);

    glViewport(0, 0, width, height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustumf(-1, 1, (float) -height / width, (float) height / width, 3, 7);

    glClearColor(.1f, .1f, .15f, 1.f);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glPointSize(BALL_RADIUS * 2 * width);
    glLineWidth(WALL_THICKNESS * 2 * width);

    for (int i = 0; i < sizeof(gStuff) / sizeof(GLObject*); i++) {
        gStuff[i]->setup(*this);
    }

    gBall.setCollisionHandler(collisionCallback);

    gBall.run();
}

GLScreen::~GLScreen() {
    gBall.stop();
}

void GLScreen::draw() {
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, -3.001, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f);

    for (int i = 0; i < sizeof(gStuff) / sizeof(GLObject*); i++) {
        gStuff[i]->draw();
    }
}

static void onTiltChanged(float tilt) {
    gBall.onTiltChanged(tilt);
}

extern "C" {

    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        gJavaVM = vm;
        return JNI_VERSION_1_4;
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_ArkanoidActivity_registerCallback(JNIEnv * env, jobject obj) {
        gCallbackObject = env->NewGlobalRef(obj);
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_ArkanoidActivity_unregisterCallback(JNIEnv * env, jobject obj) {
        env->DeleteGlobalRef(gCallbackObject);
        gCallbackObject = NULL;
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_NativeGLRenderer_nativeSetup(JNIEnv * env, jobject obj, jint width, jint height) {
        if (gScreen) delete gScreen;
        gScreen = new GLScreen(width, height);
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_NativeGLRenderer_nativeDraw(JNIEnv * env, jobject obj) {
        gScreen->draw();
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_GLView_nativeOnTiltChanged(JNIEnv * env, jobject obj, jfloat tilt) {
        onTiltChanged(tilt);
    }

    JNIEXPORT void JNICALL Java_com_praetoriandroid_arkanoidtest_GLView_nativeClear(JNIEnv * env, jobject obj) {
        delete gScreen;
        gScreen = NULL;
    }

};
